﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SharedGenomics.Annotations
{
    public class AnnotationTranscoder
    {
        /// <summary>
        /// Use .Net binary serialization to serialize a value to a byte array.  They should be deserialized in the same way.
        /// </summary>
        /// <param name="annotation"></param>
        /// <returns></returns>
        //TODO: move to workbench only?
        public byte[] EncodeAnnotation(IAnnotation annotation)
        {
            if (null == annotation)
                return null;
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, annotation);
            return ms.ToArray();
        }

        public IAnnotation DecodeAnnotation(byte[] encodedAnnotation)
        {
            if (null == encodedAnnotation)
                return null;
            MemoryStream ms = new MemoryStream(encodedAnnotation);
            BinaryFormatter bf = new BinaryFormatter();
            IAnnotation annotation = (IAnnotation)bf.Deserialize(ms);
            return annotation;
        }

        public byte[] EncodeAnnotationValueArray(AnnotationValue[] values)
        {
            // Ensure the bytes are always produced in the same order.  To do that, we have to sort the keys.
            Array.Sort(values, new Comparison<AnnotationValue>((x, y) => String.Compare(x.Key.Key, y.Key.Key)));
            string[] orderedKeys = new string[values.Length];
            object[] orderedObjects = new object[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                orderedKeys[i] = values[i].Key.Key;
                orderedObjects[i] = values[i].Value;
            }
            object[] rawKey = new object[] { orderedKeys, orderedObjects };
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, rawKey);
            return ms.ToArray();
        }

        public AnnotationValue[] DecodeAnnotationValueArray(byte[] encodedValues)
        {
            if (null == encodedValues)
                return null;
            MemoryStream ms = new MemoryStream(encodedValues);
            BinaryFormatter bf = new BinaryFormatter();
            object[] ary = (object[])bf.Deserialize(ms);
            string[] orderedKeys = (string[])ary[0];
            object[] orderedObjects = (object[])ary[1];
            AnnotationValue[] values = new AnnotationValue[orderedKeys.Length];
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = new AnnotationValue(orderedKeys[i], orderedObjects[i]);
            }
            return values;
        }

        /// <summary>
        /// Given that encodedKey is a byte stream representing a key value for annotationKey, return the decoded version.
        /// </summary>
        /// <param name="annotationKey"></param>
        /// <param name="encodedKey"></param>
        /// <returns>The decoded version of the key, or throws an exception (pretty much any exception, it is deliberately not wrapped) if it can't.</returns>
        public object DecodeKey(string annotationKey, byte[] encodedKey)
        {
            // TODO: Work out a way to generalise this.
            if (AnnotationKey.EntrezGeneId.Key.Equals(annotationKey))
            {
                // Entrez genes are integers
                // TODO: This assumes endianness is not relevant.
                return BitConverter.ToInt32(encodedKey, 0);
            }
            else if (AnnotationKey.GeneName.Key.Equals(annotationKey))
            {
                // Gene names are strings
                return new StreamReader(new MemoryStream(encodedKey), Encoding.UTF8).ReadToEnd();
            }
            else if (AnnotationKey.SnpName.Key.Equals(annotationKey))
            {
                // SNP names are strings
                return new StreamReader(new MemoryStream(encodedKey), Encoding.UTF8).ReadToEnd();
            }
            else
            {
                throw new NotImplementedException();
            }
        }
    }
}
